﻿using System;
using System.Collections.Generic;
using Windows.Devices.Input;
using Windows.Foundation;
using Windows.System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using LinkPad.Shapes;

namespace LinkPad.Handlers
{
    class ImageMover : ICanvasActionPublicHandler, ISubEditor
    {
        #region Nested types

        private class SelectedImageInfo
        {
            public double OrigX;
            public double OrigY;

            public double OrigWidth;
            public double OrigHeight;
        }

        #endregion

        #region Fields

        private readonly ShapeManager _shapeManager;
        private readonly Canvas _canvas;
        private readonly CanvasPincher _pincher;

        private Point _pressPoint;
        private uint _penId;

        private readonly Dictionary<ManagedImage, SelectedImageInfo> _selectedImages
            = new Dictionary<ManagedImage, SelectedImageInfo>();

        #endregion

        #region Constructors

        public ImageMover(ShapeManager shapeManager)
        {
            _shapeManager = shapeManager;
            _canvas = _shapeManager.Canvas;
            _canvas.PointerWheelChanged += CanvasOnPointerWheelChanged;
            _pincher = new CanvasPincher(_canvas);
            _pincher.TransformStaged += PincherOnTransformStaged;
            _pincher.TransformChanged += PincherOnTransformChanged;

            MultiSelectStatus = MultiSelectStatus.NonMulti;
        }

        ~ImageMover()
        {
            _canvas.PointerWheelChanged -= CanvasOnPointerWheelChanged;
        }

        #endregion

        #region Properties

        #region ICanvasActionPublicHandler members

        public MultiSelectStatus MultiSelectStatus { get; set; }

        #endregion

        #region ISubEditor members

        public Editor Parent
        {
            get;
            set;
        }

        public bool HasSelected { get { return _selectedImages.Count > 0; } }

        #endregion

        #endregion

        #region

        #region ICanvasActionPublicHandler members

        public void HandlePointerPressed(object sender, PointerRoutedEventArgs e)
        {
            var pt = e.GetCurrentPoint(_canvas);

            TrySelect(pt.Position, e.KeyModifiers == VirtualKeyModifiers.Control);

            if (_selectedImages.Count == 0)
            {
                return;
            }

            var pointerDevType = e.Pointer.PointerDeviceType;

            if (pointerDevType == PointerDeviceType.Pen ||
                pointerDevType == PointerDeviceType.Mouse &&
                pt.Properties.IsLeftButtonPressed
                || pointerDevType == PointerDeviceType.Touch)
            {
                _pressPoint = pt.Position;

                foreach (var p in _selectedImages)
                {
                    var image = p.Key;
                    var info = p.Value;
                    info.OrigX = image.X;
                    info.OrigY = image.Y;
                }

                _penId = pt.PointerId;
            }
            // NOTE in case that the handled flag should not be used for indicating the 
            //      selection we should add a separate flag (such as HasObjectSelected) 
            //      to ICanvasActionPublicHandler for that purpose
            e.Handled = true;
        }

        public void HandlePointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (e.Pointer.PointerId != _penId)
            {
                return;
            }

            var pt = e.GetCurrentPoint(_canvas);

            var deltaX = pt.Position.X - _pressPoint.X;
            var deltaY = pt.Position.Y - _pressPoint.Y;

            foreach (var p in _selectedImages)
            {
                var image = p.Key;
                var info = p.Value;
                image.X = info.OrigX + deltaX;
                image.Y = info.OrigY + deltaY;
            }
        }

        public void HandlePointerReleased(object sender, PointerRoutedEventArgs e)
        {
            _penId = 0;
        }

        public void HandlePointerExited(object sender, PointerRoutedEventArgs e)
        {
        }

        public void HandleCanvasTapped(object sender, TappedRoutedEventArgs e)
        {
            // not doing anything here to avoid interfering with multiple selection
        }

        public void HandleCanvasDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
        {
        }

        public void HandleDeactivated()
        {
            DeselectAll();
        }

        public void DeselectAll()
        {
            foreach (var p in _selectedImages)
            {
                var image = p.Key;
                var info = p.Value;
                               
                image.Deselect();
            }
            _selectedImages.Clear();

            UpdateGloablSetting();
        }

        public void DeleteAllSelected()
        {
            foreach (var selected in _selectedImages.Keys)
            {
                _shapeManager.RemoveShape(selected);
            }
        }

        #endregion

        private void TrySelect(Point pt, bool multiSelectKey = false)
        {
            ManagedImage selected = null;
            foreach (var shape in _shapeManager.Shapes)
            {
                var image = shape as ManagedImage;
                if (image != null && image.BoundingBox.Contains(pt))
                {
                    selected = image;
                    break;
                }
            }

            if (multiSelectKey)
            {
                MultiSelectStatus = MultiSelectStatus.Multi;
            }
            else if (selected == null)
            {
                MultiSelectStatus = MultiSelectStatus.Empty;
            }
            else if (!_selectedImages.ContainsKey(selected))
            {
                DeselectAll();
                MultiSelectStatus = MultiSelectStatus.NonMultiResetAll;
            }

            if (selected != null)
            {
                SelectImage(selected);
            }

            UpdateGloablSetting();
        }

        private void SelectImage(ManagedImage image)
        {
            if (_selectedImages.ContainsKey(image))
            {
                return;
            }
            // TODO shade the image to indicate it's been selected
            // ...

            image.Select();

            _selectedImages.Add(image, new SelectedImageInfo());
        }

        private void PincherOnTransformChanged(double k1, double k2, double d1, double d2)
        {
            if (!HasSelected || Parent.HasOtherSelected(this))
            {
                return;
            }

            if (_pincher.CurrentCount < 2)
            {
                return;
            }

            // k1==k2
            foreach (var pair in _selectedImages)
            {
                var image = pair.Key;
                var imageInfo = pair.Value;
                image.Width = imageInfo.OrigWidth*k1;
                image.Height = imageInfo.OrigHeight*k2;
            }
        }

        private void PincherOnTransformStaged()
        {
            if (!HasSelected || Parent.HasOtherSelected(this))
            {
                return;
            }

            foreach (var pair in _selectedImages)
            {
                var image = pair.Key;
                var imageInfo = pair.Value;
                imageInfo.OrigWidth = image.ActualWidth;
                imageInfo.OrigHeight = image.ActualHeight;
            }

            if (_pincher.CurrentCount < 2)
            {
                _pincher.ResetTransform();
            }
        }

        private void CanvasOnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            if (!HasSelected || Parent.HasOtherSelected(this))
            {
                return;
            }

            var point = e.GetCurrentPoint(_canvas);
            var wheelDelta = point.Properties.MouseWheelDelta;
            var k = Math.Pow(1.06, wheelDelta > 0 ? 1 : -1);
            foreach (var pair in _selectedImages)
            {
                var image = pair.Key;
                image.Width = image.ActualWidth * k;
                image.Height = image.ActualHeight * k;
            }
        }

        private void UpdateGloablSetting()
        {
            if (HasSelected && !Parent.HasOtherSelected(this))
            {
                Parent.GlobalController.EnablePinching = false;
                Parent.GlobalController.EnableWheeling = false;
            }
            else
            {
                Parent.GlobalController.EnablePinching = true;
                Parent.GlobalController.EnableWheeling = true;
            }
        }

        #endregion
    }
}
